#include <atomic>
#include <thread>
#include <vector>
#include <iostream>
#include <condition_variable>
#include <mutex>
#include <queue>
#include <optional>

class Semaphore {
public:
    explicit Semaphore(int count) : count(count) {}
    void P() {
        std::unique_lock<std::mutex> lk(mtx);
        cv.wait(lk, [&]{ return count > 0; });
        --count;
    }
    void V() {
        {
            std::lock_guard<std::mutex> lk(mtx);
            ++count;
        }
        cv.notify_one();
    }
private:
    int count;
    std::mutex mtx;
    std::condition_variable cv;
};

template <typename T>
class BoundedBuffer {
public:
    BoundedBuffer(size_t capacity) : capacity(capacity), empty(capacity), full(0) {}

    void put(const T& x) {
        empty.P();
        {
            std::lock_guard<std::mutex> lk(mtx);
            q.push(x);
        }
        full.V();
    }

    T get() {
        full.P();
        std::lock_guard<std::mutex> lk(mtx);
        T x = q.front();
        q.pop();
        empty.V();
        return x;
    }

private:
    size_t capacity;
    std::queue<T> q;
    std::mutex mtx;
    Semaphore empty;
    Semaphore full;
};

int main() {
    BoundedBuffer<int> buffer1(10);
    BoundedBuffer<int> buffer2(10);

    std::atomic<bool> running{true};

    // get: produce into buffer1
    std::thread get_thr([&]{
        for (int i = 1; i <= 100; ++i) {
            buffer1.put(i);
        }
        running.store(false);
    });

    // copy: consume from buffer1, process, produce into buffer2
    std::thread copy_thr([&]{
        while (running.load() || true) {
            // try to get item from buffer1 until get_thr finishes and buffer1 would be empty
            int x = buffer1.get();
            int y = x * 2; // process
            buffer2.put(y);
            if (!running.load() && x == 100) break;
        }
    });

    // put: consume from buffer2 and print
    std::thread put_thr([&]{
        for (int i = 1; i <= 100; ++i) {
            int y = buffer2.get();
            if (i <= 5) { // 限制输出数量，避免刷屏
                std::cout << "print: " << y << std::endl;
            }
        }
    });

    get_thr.join();
    copy_thr.join();
    put_thr.join();

    std::cout << "PV pipeline finished" << std::endl;
    return 0;
}